package com.basker.pisces.domain.property.meta;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.basker.pisces.core.meta.annotation.CollectionField;
import com.basker.pisces.core.meta.annotation.DataField;
import com.basker.pisces.domain.ISupportEntityBuild;
import com.basker.pisces.domain.constant.DomainConstants.ObjectErrorCode;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.enums.EnumTypeManager;
import com.basker.pisces.domain.property.InvalidPropertyValueException;
import com.basker.pisces.domain.value.StatusValue;
import com.basker.pisces.domain.value.meta.EnumOption;
import com.basker.pisces.domain.value.meta.EnumType;
import com.basker.pisces.domain.value.meta.StatusOption;
import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.resource.StringResources;
import com.basker.pisces.service.exception.IObjectErrorsWrapper.ObjectError;
import com.basker.pisces.utils.NumberUtils;

public class StatusProperty extends Property<StatusValue> implements ISupportEntityBuild {

    @CollectionField
    private List<StatusOption> options = new ArrayList<>();
    @DataField
    private String enumTypeName;

    private transient Map<Integer, StatusOption> optionMap = new HashMap<>();

    public void addOption(Integer statusValue, String displayName) {
        this.options.add(new StatusOption(statusValue, displayName));
    }

    public void addOption(StatusOption option) {
        this.options.add(option);
    }

    @Override
    public void build(Entity entity) {
        Assert.notEmpty(options, "StatusProperty '" + this.toString() + "' options is empty!");

        this.optionMap.clear();

        for (StatusOption option : options) {
            Integer statusValue = option.getStatusValue();

            Assert.notNull(statusValue, "option's statusValue is required!");

            StatusOption existOption = this.optionMap.get(statusValue);
            if (existOption != null) {
                throw ExceptionFactory.createRuntimeException("pisces-domain.StatusProperty.build-001",
                        "StatusOption:{0} is exist!", statusValue);
            }

            this.optionMap.put(statusValue, option);
        }
    }

    public boolean containsStatusValue(int target) {
        return this.options.stream().anyMatch(o -> Objects.equals(o.getStatusValue(), target));
    }

    @Override
    public StatusValue createValue(Object rawValue) {
        StatusOption option = null;

        if (rawValue != null) {
            option = this.getOption(NumberUtils.parseInt(rawValue));
        }

        if (option == null) {
            return StatusValue.EMPTY;
        } else {
            return new StatusValue(option);
        }
    }

    /**
     * 获取对应的枚举类型
     *
     * @return
     * @see EnumTypeManager
     */
    public String getEnumTypeName() {
        return enumTypeName;
    }

    public void setEnumTypeName(String enumTypeName) {
        this.enumTypeName = enumTypeName;
    }

    public StatusOption getOption(Integer statusValue) {
        if (statusValue == null) {
            return null;
        }

        return this.optionMap.get(statusValue);
    }

    public List<StatusOption> getOptions() {
        return options;
    }

    public void setOptions(List<StatusOption> options) {
        this.options = options;
    }

    /**
     * 通过{@link #getEnumTypeName()}加载选项
     */
    public final void loadEnumType() {
        if (StringUtils.isEmpty(this.getEnumTypeName())) {
            return;
        }

        this.options.clear();

        EnumType enumType = EnumTypeManager.getInstance().getEnumType(this.getEnumTypeName());
        if (enumType == null) {
            return;
        }

        try {
            StringResources.suspandResovle();

            for (EnumOption enumOption : enumType.getOptions()) {
                this.addOption(NumberUtils.parseInt(enumOption.getId()), enumOption.getDisplayName());
            }
        } finally {
            StringResources.resumeResovle();
        }
    }

    @Override
    public void setValue(Object dataObject, Object inputValue) throws InvalidPropertyValueException {
        StatusOption option = null;

        if (inputValue != null) {
            option = this.getOption(NumberUtils.parseInt(inputValue));

            if (option == null) {
                String errorMessage = this.getInvalidInputValueMessage(this.toString(), inputValue);
                ObjectError objectError = new ObjectError(ObjectErrorCode.STATUS_INVALID, dataObject, "", errorMessage);

                throw new InvalidPropertyValueException(Arrays.asList(objectError));
            }
        }

        this.setRawValue(dataObject, inputValue);
    }

    @Override
    protected String getDefaultInvalidInputValueMessage(String propertyDesc, Object inputValue) {
        return StringResources.get("pisces-domain.StatusProperty.InvalidOptionValue", propertyDesc, inputValue);
    }

}
